/*
 * Copyright (C) 2008-2012  Paulo Cesar Pereira de Andrade.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Authors:
 *	Paulo Cesar Pereira de Andrade
 */

#include "exl.h"

#define P(n)		eval_vector->v.ptr + (n)
#define N(n)		eval_vector->v.ptr[n]

#define collect(n)	edel_object(P(n))
#define coerced(n)	cache_mask |= 1 << n

#define _pwp(v)		*(eword_t *)v
#define pwp(v)		_pwp(N(v))
#define get_word(n)	eget_word(P(n))

#define _pdp(v)		*(efloat_t *)v
#define pdp(v)		_pdp(N(v))
#define get_float(n)	eget_float(P(n))

#define _rat(v)		(erat_t)v
#define _prat(v)	*(erat_t *)v
#define rat(v)		_rat(N(v))
#define get_rat(n)	eget_rat(P(n))

#define _mpz(v)		(empz_t)(v)
#define mpz(v)		(empz_t)(N(v))
#define get_mpz(n)	eget_mpz(P(n))

#define _pddp(v)	*(ecdd_t *)v
#define pddp(v)		_pddp(N(v))
#define get_cdd(n)	eget_cdd(P(n))

#define _mpq(v)		(empq_t)(v)
#define mpq(v)		(empq_t)(N(v))
#define get_mpq(n)	eget_mpq(P(n))

#define _mpr(v)		(empr_t)(v)
#define mpr(v)		(empr_t)(N(v))
#define get_mpr(n)	eget_mpr(P(n))

#define _cqq(v)		(ecqq_t)(v)
#define cqq(v)		(ecqq_t)(N(v))
#define get_cqq(n)	eget_cqq(P(n))

#define _mpc(v)		(empc_t)(v)
#define mpc(v)		(empc_t)(N(v))
#define get_mpc(n)	eget_mpc(P(n))

/*
 * Prototypes
 */
static void
eval_unary_setup(eobject_t value);

static void
eval_binary_setup(eobject_t lvalue, eobject_t rvalue);

static void
check_real(east_t *ast);

static void
check_integer(east_t *ast);

static eint32_t
shift_power(east_t *ast);

static eobject_t
cleanup(void);

static eobject_t
canonicalize(void);

/*
 * Initialization
 */
static evector_t	*eval_vector;
static eint32_t		 cache_mask;

/*
 * Implementation
 */
void
init_eval(void)
{
    eadd_root((eobject_t *)&eval_vector);
    enew_vector((eobject_t *)&eval_vector, t_void, 8);
}

void
finish_eval(void)
{
    erem_root((eobject_t *)&eval_vector);
}

void
ecoerce(eobject_t *pointer, etype_t type, eobject_t value)
{
    union {
	eint64_t	 i;
	efloat32_t	 f;
	efloat_t	 d;
    } data;

    switch (type) {
	case t_int8:		case t_uint8:
	case t_int16:		case t_uint16:
	case t_int32:		case t_uint32:
	case t_int64:		case t_uint64:
	    switch (etype(value)) {
		case t_word:
		    data.i = _pwp(value);
		    break;
		case t_float:
		    data.i = _pdp(value);
		    break;
		case t_mpz:
		    data.i = empz_get_ll(_mpz(value));
		    break;
		case t_mpq:
		    data.i = empq_get_ll(_mpq(value));
		    break;
		case t_mpr:
		    data.i = empr_get_ll(_mpr(value));
		    break;
		case t_cdd:
		    data.i = real(_pddp(value));
		    break;
		case t_cqq:
		    data.i = empq_get_ll(cqq_realref(_cqq(value)));
		    break;
		default:
		    assert(etype(value) == t_mpc);
		    data.i = empr_get_ll(mpc_realref(_mpc(value)));
		    break;
	    }
	    break;
	case t_float32:		case t_float64:
	    switch (etype(value)) {
		case t_word:
		    data.d = _pwp(value);
		    break;
		case t_float:
		    data.d = _pdp(value);
		    break;
		case t_mpz:
		    data.d = mpz_get_d(_mpz(value));
		    break;
		case t_mpq:
		    data.d = mpq_get_d(_mpq(value));
		    break;
		case t_mpr:
		    data.d = mpfr_get_d(_mpr(value), thr_rnd);
		    break;
		case t_cdd:
		    data.d = real(_pddp(value));
		    break;
		case t_cqq:
		    data.d = mpq_get_d(cqq_realref(_cqq(value)));
		    break;
		default:
		    assert(etype(value) == t_mpc);
		    data.i = mpfr_get_d(mpc_realref(_mpc(value)), thr_rnd);
		    break;
	    }
	    break;
	default:
	    break;
    }

    switch (type) {
	case t_int8:
	    eget_word(pointer);		pointer = *pointer;
	    _pwp(pointer) = (eint8_t)data.i;
	    break;
	case t_uint8:
	    eget_word(pointer);		pointer = *pointer;
	    _pwp(pointer) = (euint8_t)data.i;
	    break;
	case t_int16:
	    eget_word(pointer);		pointer = *pointer;
	    _pwp(pointer) = (eint16_t)data.i;
	    break;
	case t_uint16:
	    eget_word(pointer);		pointer = *pointer;
	    _pwp(pointer) = (euint16_t)data.i;
	    break;
	case t_int32:
	    eget_word(pointer);		pointer = *pointer;
	    _pwp(pointer) = (eint32_t)data.i;
	    break;
	case t_uint32:
#if __WORDSIZE == 32
	    if ((euword_t)data.i != data.i) {
		eget_mpz(pointer);	pointer = *pointer;
		mpz_set_ui(_mpz(pointer), (euword_t)data.i);
	    }
	    else
#endif
	    {
		eget_word(pointer);	pointer = *pointer;
		_pwp(pointer) = (euint32_t)data.i;
	    }
	    break;
	case t_int64:
#if __WORDSIZE == 32
	    if ((eword_t)data.i != data.i) {
		eget_mpz(pointer);	pointer = *pointer;
		empz_set_ll(_mpz(pointer), data.i);
	    }
	    else
#endif
	    {
		eget_word(pointer);	pointer = *pointer;
		_pwp(pointer) = (eword_t)data.i;
	    }
	    break;
	case t_uint64:
	    if ((euword_t)data.i != data.i) {
		eget_mpz(pointer);	pointer = *pointer;
		mpz_set_ui(_mpz(pointer), (euword_t)data.i);
	    }
	    else {
		eget_word(pointer);	pointer = *pointer;
		_pwp(pointer) = (euword_t)data.i;
	    }
	    break;
	case t_float32:
	    data.f = data.d;
	    data.d = data.f;
	case t_float:
	    eget_float(pointer);	pointer = *pointer;
	    _pdp(pointer) = data.d;
	    break;
	default:
	    ecopy(pointer, value);
	    break;
    }
}

void
ecopy(eobject_t *pointer, eobject_t value)
{
    switch (etype(value)) {
	case t_word:
	    eget_word(pointer);		pointer = *pointer;
	    _pwp(pointer) = _pwp(value);
	    break;
	case t_float:
	    eget_float(pointer);	pointer = *pointer;
	    _pdp(pointer) = _pdp(value);
	    break;
	case t_rat:
	    eget_rat(pointer);		pointer = *pointer;
	    _prat(pointer) = _prat(value);
	    break;
	case t_mpz:
	    eget_mpz(pointer);		pointer = *pointer;
	    mpz_set(_mpz(pointer), _mpz(value));
	    break;
	case t_mpq:
	    eget_mpq(pointer);		pointer = *pointer;
	    mpq_set(_mpq(pointer), _mpq(value));
	    break;
	case t_mpr:
	    eget_mpr(pointer);		pointer = *pointer;
	    mpfr_set(_mpr(pointer), _mpr(value), thr_rnd);
	    break;
	case t_cdd:
	    eget_cdd(pointer);		pointer = *pointer;
	    _pddp(pointer) = _pddp(value);
	    break;
	case t_cqq:
	    eget_cqq(pointer);		pointer = *pointer;
	    cqq_set(_cqq(pointer), _cqq(value));
	    break;
	default:
	    assert(etype(value) == t_mpc);
	    eget_mpc(pointer);		pointer = *pointer;
	    mpc_set(_mpc(pointer), _mpc(value), thr_rndc);
	    break;
    }
}

ebool_t
efalse_p(eobject_t object)
{
    if (object == null)
	return (true);
    switch (etype(object)) {
	case t_word:
	    return (_pwp(object) == 0);
	case t_float:
	    return (_pdp(object) == 0.0);
	case t_mpr:
	    return (mpfr_zero_p(_mpr(object)) != 0);
	default:
	    return (false);
    }
}

void
eeval_unary(east_t *ast)
{
    ebool_t		lnum;
    /* object is gc proteced in result of eeval_xyz() */
    eobject_t		object;

    switch (ast->token) {
	case tok_plus:
	    if (ast->l.ast->token == tok_number) {
		emove_left_ast_up(ast);
		ast->token = tok_number;
	    }
	    return;
	case tok_neg:		case tok_not:
	    lnum = ast->l.ast->token == tok_number;
	    break;
	case tok_com:
	    if ((lnum = ast->l.ast->token == tok_number))
		check_integer(ast->l.ast);
	    break;
	default:
	    abort();
    }

    if (!lnum)
	return;

    eval_unary_setup(ast->l.ast->l.value);
    switch (ast->token) {
	case tok_neg:		object = eeval_neg();		break;
	case tok_not:		object = eeval_not();		break;
	case tok_com:		object = eeval_com();		break;
	default:		abort();
    }
    edel_object(&ast->l.value);
    ast->l.value = object;
    ast->token = tok_number;
}

void
eeval_binary(east_t *ast)
{
    ebool_t		lnum;
    ebool_t		rnum;
    eobject_t		object;

    switch (ast->token) {
	case tok_ne:		case tok_eq:
	case tok_add:		case tok_sub:
	case tok_mul:		case tok_div:
	    lnum = ast->l.ast->token == tok_number;
	    rnum = ast->r.ast->token == tok_number;
	    break;
	case tok_lt:		case tok_le:
	case tok_ge:		case tok_gt:
	case tok_trunc2:	case tok_rem:
	case tok_complex:
	    if ((lnum = ast->l.ast->token == tok_number))
		check_real(ast->l.ast);
	    if ((rnum = ast->r.ast->token == tok_number))
		check_real(ast->r.ast);
	    break;
	case tok_and:		case tok_or:
	case tok_xor:
	    if ((lnum = ast->l.ast->token == tok_number))
		check_integer(ast->l.ast);
	    if ((rnum = ast->r.ast->token == tok_number))
		check_integer(ast->r.ast);
	    break;
	default:
	    abort();
    }

    if (!lnum || !rnum)
	return;

    eval_binary_setup(ast->l.ast->l.value, ast->r.ast->l.value);
    switch (ast->token) {
	case tok_ne:		object = eeval_ne();		break;
	case tok_lt:		object = eeval_lt();		break;
	case tok_le:		object = eeval_le();		break;
	case tok_eq:		object = eeval_eq();		break;
	case tok_ge:		object = eeval_ge();		break;
	case tok_gt:		object = eeval_gt();		break;
	case tok_and:		object = eeval_and();		break;
	case tok_or:		object = eeval_or();		break;
	case tok_xor:		object = eeval_xor();		break;
	case tok_add:		object = eeval_add();		break;
	case tok_sub:		object = eeval_sub();		break;
	case tok_mul:		object = eeval_mul();		break;
	case tok_div:		object = eeval_div();		break;
	case tok_trunc2:	object = eeval_trunc2();	break;
	case tok_rem:		object = eeval_rem();		break;
	case tok_complex:	object = eeval_complex();	break;
	default:		abort();
    }
    edel_object(&ast->l.value);
    edel_object(&ast->r.value);
    ast->l.value = object;
    ast->token = tok_number;
}

void
eeval_shift(east_t *ast)
{
    ebool_t		lnum;
    ebool_t		rnum;
    eint32_t		shift;
    eobject_t		object;

    switch (ast->token) {
	case tok_mul2:		case tok_div2:
	    lnum = ast->l.ast->token == tok_number;
	    break;
	case tok_shl:		case tok_shr:
	    if ((lnum = ast->l.ast->token == tok_number))
		check_real(ast->l.ast);
	    break;
	default:
	    abort();
    }

    if ((rnum = ast->r.ast->token == tok_number))
	shift = shift_power(ast->r.ast);

    if (!lnum || !rnum)
	return;

    eval_unary_setup(ast->l.ast->l.value);
    if (shift >= 0) {
	switch (ast->token) {
	    case tok_mul2:	object = eeval_mul2(shift);	break;
	    case tok_div2:	object = eeval_div2(shift);	break;
	    case tok_shl:	object = eeval_shl(shift);	break;
	    case tok_shr:	object = eeval_shr(shift);	break;
	    default:		abort();
	}
    }
    else {
	shift = -shift;
	switch (ast->token) {
	    case tok_mul2:	object = eeval_div2(shift);	break;
	    case tok_div2:	object = eeval_mul2(shift);	break;
	    case tok_shl:	object = eeval_shr(shift);	break;
	    case tok_shr:	object = eeval_shl(shift);	break;
	    default:		abort();
	}
    }
    edel_object(&ast->l.value);
    edel_object(&ast->r.value);
    ast->l.value = object;
    ast->token = tok_number;
}

#define boolean_nothing		0
#define boolean_false		1
#define boolean_true		2
#define boolean_left		3
#define boolean_right		4
void
eeval_boolean(east_t *ast)
{
    eint32_t		state;

    state = boolean_nothing;
    if (ast->l.ast->token == tok_number) {
	if (efalse_p(ast->l.ast->l.value)) {
	    if (ast->token == tok_andand)
		state = boolean_false;
	    else
		state = boolean_right;
	}
	else {
	    if (ast->token == tok_andand)
		state = boolean_right;
	    else
		state = boolean_true;
	}
	if (state == boolean_right && ast->r.ast->token == tok_number) {
	    if (efalse_p(ast->r.ast->l.value))
		state = boolean_false;
	    else
		state = boolean_true;
	}
    }
    else if (ast->r.ast->token == tok_number) {
	if (efalse_p(ast->r.ast->l.value)) {
	    if (ast->token == tok_oror)
		state = boolean_left;
	    else {
		/* expr && 0 -> (expr, 0) */
		ast->l.ast->next = ast->r.ast;
		ast->r.ast = null;
		ast->token = tok_list;
	    }
	}
	else
	    state = boolean_left;
    }
    switch (state) {
	case boolean_nothing:
	    break;
	case boolean_false:	case boolean_true:
	    edel_object(&ast->l.value);
	    edel_object(&ast->r.value);
	    eget_word(&ast->l.value);
	    *(eword_t *)ast->l.value = state == boolean_true;
	    ast->token = tok_number;
	    break;
	case boolean_left:
	    emove_ast_up_full(ast, ast->l.ast);
	    break;
	default:
	    assert(state == boolean_right);
	    emove_ast_up_full(ast, ast->r.ast);
	    break;
    }
}
#undef boolean_nothing
#undef boolean_false
#undef boolean_true
#undef boolean_left
#undef boolean_right

eobject_t
eeval_neg(void)
{
    switch (etype(N(1))) {
	case t_word:
	    if (pwp(1) == MININT) {
		get_mpz(0);
		mpz_set_ui(mpz(0), MININT);
	    }
	    else {
		get_word(0);
		pwp(0) = -pwp(1);
	    }
	    break;
	case t_float:
	    get_float(0);
	    pdp(0) = -pdp(1);
	    break;
	case t_rat:
	    if (erat_num(rat(1)) == MININT) {
		get_mpq(0);
		mpq_set_ui(mpq(0), erat_num(rat(1)), erat_den(rat(1)));
		mpq_canonicalize(mpq(0));
	    }
	    else {
		get_rat(0);
		erat_num(rat(0)) = erat_num(rat(1));
	    }
	    break;
	case t_mpz:
	    get_mpz(0);
	    mpz_neg(mpz(0), mpz(1));
	    break;
	case t_mpq:
	    get_mpq(0);
	    mpq_neg(mpq(0), mpq(1));
	    break;
	case t_mpr:
	    get_mpr(0);
	    mpfr_neg(mpr(0), mpr(1), thr_rnd);
	    break;
	case t_cdd:
	    get_cdd(0);
	    pddp(0) = -pddp(1);
	    break;
	case t_cqq:
	    get_cqq(0);
	    cqq_neg(cqq(0), cqq(1));
	    break;
	default:
	    get_mpc(0);
	    mpc_neg(mpc(0), mpc(1), thr_rndc);
	    break;
    }

    return (canonicalize());
}

eobject_t
eeval_not(void)
{
    eword_t		w;

    switch (etype(N(1))) {
	case t_word:
	    w = !pwp(1);
	    break;
	case t_float:
	    w = !pdp(1);
	    break;
	case t_rat:
	    w = erat_num(rat(1)) != 0;
	    break;
	case t_mpq:
	    w = mpq_sgn(mpq(1)) != 0;
	    break;
	case t_mpr:
	    w = !!mpfr_zero_p(mpr(1));
	    break;
	default:
	    w = 0;
	    break;
    }
    get_word(0);
    pwp(0) = w;

    return (cleanup());
}

eobject_t
eeval_com(void)
{
    switch (etype(N(1))) {
	case t_word:
	    get_word(0);
	    pwp(0) = ~pwp(1);
	    break;
	case t_mpz:
	    get_mpz(0);
	    mpz_com(mpz(0), mpz(1));
	    break;
	default:
	    abort();
    }

    return (canonicalize());
}

eobject_t
eeval_ne(void)
{
    eword_t		w;

    switch (etype(N(1))) {
	case t_word:
	    w = pwp(1) != pwp(2);
	    break;
	case t_float:
	    w = pdp(1) != pdp(2);
	    break;
	case t_rat:
	    w = erat_cmp(rat(1), rat(2)) != 0;
	    break;
	case t_mpz:
	    w = mpz_cmp(mpz(1), mpz(2)) != 0;
	    break;
	case t_mpq:
	    w = mpq_cmp(mpq(1), mpq(2)) != 0;
	    break;
	case t_mpr:
	    w = !mpfr_equal_p(mpr(1), mpr(2));
	    break;
	case t_cdd:
	    w = pddp(1) != pddp(2);
	    break;
	case t_cqq:
	    w = ((mpq_cmp(cqq_realref(cqq(1)),
			  cqq_realref(cqq(2))) != 0) ||
		 (mpq_cmp(cqq_imagref(cqq(1)),
			  cqq_imagref(cqq(2))) != 0));
	    break;
	default:
	    w = (!mpfr_equal_p(mpc_realref(mpc(1)),
			       mpc_realref(mpc(2))) ||
		 !mpfr_equal_p(mpc_imagref(mpc(1)),
			       mpc_imagref(mpc(2))));
	    break;
    }
    get_word(0);
    pwp(0) = w;

    return (cleanup());
}

eobject_t
eeval_lt(void)
{
    eword_t		w;

    switch (etype(N(1))) {
	case t_word:
	    w = pwp(1) < pwp(2);
	    break;
	case t_float:
	    w = pdp(1) < pdp(2);
	    break;
	case t_rat:
	    w = erat_cmp(rat(1), rat(2)) < 0;
	    break;
	case t_mpz:
	    w = mpz_cmp(mpz(1), mpz(2)) < 0;
	    break;
	case t_mpq:
	    w = mpq_cmp(mpq(1), mpq(2)) < 0;
	    break;
	case t_mpr:
	    w = mpfr_less_p(mpr(1), mpr(2));
	    break;
	default:
	    abort();
    }
    get_word(0);
    pwp(0) = w;

    return (cleanup());
}

eobject_t
eeval_le(void)
{
    eword_t		w;

    switch (etype(N(1))) {
	case t_word:
	    w = pwp(1) <= pwp(2);
	    break;
	case t_float:
	    w = pdp(1) <= pdp(2);
	    break;
	case t_rat:
	    w = erat_cmp(rat(1), rat(2)) <= 0;
	    break;
	case t_mpz:
	    w = mpz_cmp(mpz(1), mpz(2)) <= 0;
	    break;
	case t_mpq:
	    w = mpq_cmp(mpq(1), mpq(2)) <= 0;
	    break;
	case t_mpr:
	    w = mpfr_lessequal_p(mpr(1), mpr(2));
	    break;
	default:
	    abort();
    }
    get_word(0);
    pwp(0) = w;

    return (cleanup());
}

eobject_t
eeval_eq(void)
{
    eword_t		w;

    switch (etype(N(1))) {
	case t_word:
	    w = pwp(1) == pwp(2);
	    break;
	case t_float:
	    w = pdp(1) == pdp(2);
	    break;
	case t_rat:
	    w = erat_cmp(rat(1), rat(2)) == 0;
	    break;
	case t_mpz:
	    w = mpz_cmp(mpz(1), mpz(2)) == 0;
	    break;
	case t_mpq:
	    w = mpq_cmp(mpq(1), mpq(2)) == 0;
	    break;
	case t_mpr:
	    w = mpfr_equal_p(mpr(1), mpr(2));
	    break;
	case t_cdd:
	    w = pddp(1) == pddp(2);
	    break;
	case t_cqq:
	    w = ((mpq_cmp(cqq_realref(cqq(1)),
			  cqq_realref(cqq(2))) == 0) &&
		 (mpq_cmp(cqq_imagref(cqq(1)),
			  cqq_imagref(cqq(2))) == 0));
	    break;
	default:
	    w = (mpfr_equal_p(mpc_realref(mpc(1)),
			      mpc_realref(mpc(2))) &&
		 mpfr_equal_p(mpc_imagref(mpc(1)),
			      mpc_imagref(mpc(2))));
	    break;
    }
    get_word(0);
    pwp(0) = w;

    return (cleanup());
}

eobject_t
eeval_ge(void)
{
    eword_t		w;

    switch (etype(N(1))) {
	case t_word:
	    w = pwp(1) >= pwp(2);
	    break;
	case t_float:
	    w = pdp(1) >= pdp(2);
	    break;
	case t_rat:
	    w = erat_cmp(rat(1), rat(2)) >= 0;
	    break;
	case t_mpz:
	    w = mpz_cmp(mpz(1), mpz(2)) >= 0;
	    break;
	case t_mpq:
	    w = mpq_cmp(mpq(1), mpq(2)) >= 0;
	    break;
	case t_mpr:
	    w = mpfr_greaterequal_p(mpr(1), mpr(2));
	    break;
	default:
	    abort();
    }
    get_word(0);
    pwp(0) = w;

    return (cleanup());
}

eobject_t
eeval_gt(void)
{
    eword_t		w;
    switch (etype(N(1))) {
	case t_word:
	    w = pwp(1) > pwp(2);
	    break;
	case t_float:
	    w = pdp(1) > pdp(2);
	    break;
	case t_rat:
	    w = erat_cmp(rat(1), rat(2)) > 0;
	    break;
	case t_mpz:
	    w = mpz_cmp(mpz(1), mpz(2)) > 0;
	    break;
	case t_mpq:
	    w = mpq_cmp(mpq(1), mpq(2)) > 0;
	    break;
	case t_mpr:
	    w = mpfr_greater_p(mpr(1), mpr(2));
	    break;
	default:
	    abort();
    }
    get_word(0);
    pwp(0) = w;

    return (cleanup());
}

eobject_t
eeval_and(void)
{
    switch (etype(N(1))) {
	case t_word:
	    get_word(0);
	    pwp(0) = pwp(1) & pwp(2);
	    break;
	case t_mpz:
	    get_mpz(0);
	    mpz_and(mpz(0), mpz(1), mpz(2));
	    break;
	default:
	    abort();
    }

    return (canonicalize());
}

eobject_t
eeval_or(void)
{
    switch (etype(N(1))) {
	case t_word:
	    get_word(0);
	    pwp(0) = pwp(1) | pwp(2);
	    break;
	case t_mpz:
	    get_mpz(0);
	    mpz_ior(mpz(0), mpz(1), mpz(2));
	    break;
	default:
	    abort();
    }

    return (cleanup());
}

eobject_t
eeval_xor(void)
{
    switch (etype(N(1))) {
	case t_word:
	    get_word(0);
	    pwp(0) = pwp(1) ^ pwp(2);
	    break;
	case t_mpz:
	    get_mpz(0);
	    mpz_xor(mpz(0), mpz(1), mpz(2));
	    break;
	default:
	    abort();
    }

    return (canonicalize());
}

eobject_t
eeval_mul2(eint32_t shift)
{
    eword_t		w;

    switch (etype(N(1))) {
	case t_word:
	    if (shift < (sizeof(eword_t) << 3)) {
		w = pwp(1) << shift;
		if (likely(w >> shift == pwp(1) && w != MININT)) {
		    get_word(0);
		    pwp(0) = w;
		    goto done;
		}
	    }
	    get_mpz(0);
	    mpz_set_si(mpz(0), pwp(1));
	    mpz_mul_2exp(mpz(0), mpz(0), shift);
	    break;
	case t_float:
	    get_float(0);
	    pdp(0) = ldexp(pdp(1), shift);
	    break;
	case t_rat:
	    if (shift < (sizeof(eword_t) << 3)) {
		w = erat_num(rat(1)) << shift;
		if (likely(w >> shift == erat_num(rat(1)) && w != MININT)) {
		    get_rat(0);
		    erat_num(rat(0)) = w;
		    erat_den(rat(0)) = erat_den(rat(1));
		    goto done;
		}
	    }
	    get_mpq(0);
	    mpq_set_si(mpq(0), erat_num(rat(1)), erat_den(rat(1)));
	    mpq_mul_2exp(mpq(0), mpq(0), shift);
	    break;
	case t_mpz:
	    get_mpz(0);
	    mpz_mul_2exp(mpz(0), mpz(1), shift);
	    break;
	case t_mpq:
	    get_mpq(0);
	    mpq_mul_2exp(mpq(0), mpq(1), shift);
	    break;
	case t_mpr:
	    get_mpr(0);
	    mpfr_mul_2exp(mpr(0), mpr(1), shift, thr_rnd);
	    break;
	case t_cdd:
	    get_cdd(0);
	    real(pddp(0)) = ldexp(real(pddp(1)), shift);
	    imag(pddp(0)) = ldexp(imag(pddp(1)), shift);
	    break;
	case t_cqq:
	    get_cqq(0);
	    cqq_mul_2exp(cqq(0), cqq(1), shift);
	    break;
	default:
	    assert(etype(N(1)) == t_mpc);
	    get_mpc(0);
	    mpc_mul_2exp(mpc(0), mpc(1), shift, thr_rndc);
	    break;
    }

done:
    return (canonicalize());
}

eobject_t
eeval_div2(eint32_t shift)
{
    eword_t		w;

    switch (etype(N(1))) {
	case t_word:
	    if (shift < (sizeof(eword_t) << 3)) {
		w = pwp(1) >> shift;
		if (w << shift == pwp(1)) {
		    get_word(0);
		    pwp(0) = w;
		}
		else {
		    w = pwp(1);
		    get_rat(0);
		    erat_num(rat(0)) = w;
		    erat_den(rat(0)) = 1 << shift;
		}
		goto done;
	    }
	    get_mpq(0);
	    mpq_set_si(mpq(0), pwp(1), 1);
	    mpq_div_2exp(mpq(0), mpq(0), shift);
	    break;
	case t_float:
	    get_float(0);
	    pdp(0) = ldexp(pdp(1), -shift);
	    break;
	case t_rat:
	    if (shift < (sizeof(eword_t) << 3)) {
		w = erat_num(rat(1)) >> shift;
		if (likely(w << shift == erat_num(rat(1)) && w != MININT)) {
		    get_rat(0);
		    erat_num(rat(0)) = w;
		    erat_den(rat(0)) = erat_den(rat(1));
		    goto done;
		}
	    }
	    get_mpq(0);
	    mpq_set_si(mpq(0), erat_num(rat(1)), erat_den(rat(1)));
	    mpq_div_2exp(mpq(0), mpq(0), shift);
	    break;
	case t_mpz:
	    get_mpq(0);
	    mpq_set_z(mpq(0), mpz(1));
	    mpq_div_2exp(mpq(0), mpq(0), shift);
	    break;
	case t_mpq:
	    get_mpq(0);
	    mpq_div_2exp(mpq(0), mpq(1), shift);
	    break;
	case t_mpr:
	    get_mpr(0);
	    mpfr_div_2exp(mpr(0), mpr(1), shift, thr_rnd);
	    break;
	case t_cdd:
	    get_cdd(0);
	    real(pddp(0)) = -ldexp(real(pddp(1)), shift);
	    imag(pddp(0)) = -ldexp(imag(pddp(1)), shift);
	    break;
	case t_cqq:
	    get_cqq(0);
	    cqq_div_2exp(cqq(0), cqq(1), shift);
	    break;
	default:
	    assert(etype(N(1)) == t_mpc);
	    get_mpc(0);
	    mpc_div_2exp(mpc(0), mpc(1), shift, thr_rndc);
	    break;
    }

done:
    return (canonicalize());
}

eobject_t
eeval_shl(eint32_t shift)
{
    eword_t		w;

    switch (etype(N(1))) {
	case t_word:
	    if (shift < (sizeof(eword_t) << 3)) {
		w = pwp(1) << shift;
		if (w >> shift == pwp(1) && w != MININT) {
		    get_word(0);
		    pwp(0) = w;
		    goto done;
		}
	    }
	    get_mpz(0);
	    mpz_set_si(mpz(0), pwp(1));
	    mpz_mul_2exp(mpz(0), mpz(0), shift);
	    break;
	case t_rat:
	    if (shift < (sizeof(eword_t) << 3)) {
		w = erat_num(rat(1)) / erat_den(rat(1));
		if (w < 0)
		    --w;
		if ((w << shift) >> shift == w) {
		    get_word(0);
		    pwp(0) = w << shift;
		    goto done;
		}
	    }
	    get_mpz(0);
	    get_mpq(2);
	    mpq_set_si(mpq(2), erat_num(rat(1)), erat_den(rat(1)));
	    mpz_fdiv_q(mpz(0), mpq_numref(mpq(2)), mpq_denref(mpq(2)));
	    mpz_mul_2exp(mpz(0), mpz(0), shift);
	    collect(2);
	    break;
	case t_float:
	    get_mpz(0);
	    mpz_set_d(mpz(0), floor(pdp(1)));
	    mpz_mul_2exp(mpz(0), mpz(0), shift);
	    break;
	case t_mpz:
	    get_mpz(0);
	    mpz_mul_2exp(mpz(0), mpz(1), shift);
	    break;
	case t_mpq:
	    get_mpz(0);
	    mpz_fdiv_q(mpz(0), mpq_numref(mpq(1)), mpq_denref(mpq(1)));
	    mpz_mul_2exp(mpz(0), mpz(0), shift);
	    break;
	case t_mpr:
	    get_mpr(2);
	    mpfr_floor(mpr(2), mpr(1));
	    get_mpz(0);
	    if (unlikely(!mpfr_number_p(mpr(2))))
		ethread_kill(SIGFPE);
	    mpfr_get_z(mpz(0), mpr((2)), GMP_RNDZ);
	    mpz_mul_2exp(mpz(0), mpz(0), shift);
	    collect(2);
	    break;
	default:
	    abort();
    }

done:
    return (canonicalize());
}

eobject_t
eeval_shr(eint32_t shift)
{
    eword_t		w;

    switch (etype(N(1))) {
	case t_word:
	    get_word(0);
	    if (shift < (sizeof(eword_t) << 3))
		pwp(0) = pwp(1) >> shift;
	    else
		pwp(0) = pwp(1) >> ((sizeof(eword_t) << 3) - 1);
	    break;
	case t_float:
	    get_mpz(0);
	    mpz_set_d(mpz(0), floor(pdp(1)));
	    mpz_fdiv_q_2exp(mpz(0), mpz(0), shift);
	    break;
	case t_rat:
	    if (shift < (sizeof(eword_t) << 3)) {
		w = erat_num(rat(1)) / erat_den(rat(1));
		if (w < 0)
		    --w;
		if ((w >> shift) << shift == w) {
		    get_word(0);
		    pwp(0) = w >> shift;
		    goto done;
		}
	    }
	    get_mpz(0);
	    get_mpq(2);
	    mpq_set_si(mpq(2), erat_num(rat(1)), erat_den(rat(1)));
	    mpz_fdiv_q(mpz(0), mpq_numref(mpq(2)), mpq_denref(mpq(2)));
	    mpz_fdiv_q_2exp(mpz(0), mpz(0), shift);
	    collect(2);
	    break;
	case t_mpz:
	    get_mpz(0);
	    mpz_fdiv_q_2exp(mpz(0), mpz(1), shift);
	    break;
	case t_mpq:
	    get_mpz(0);
	    mpz_fdiv_q(mpz(0), mpq_numref(mpq(1)), mpq_denref(mpq(1)));
	    mpz_fdiv_q_2exp(mpz(0), mpz(0), shift);
	    break;
	case t_mpr:
	    get_mpr(2);
	    mpfr_floor(mpr(2), mpr(1));
	    get_mpz(0);
	    if (unlikely(!mpfr_number_p(mpr(2))))
		ethread_kill(SIGFPE);
	    mpfr_get_z(mpz(0), mpr((2)), GMP_RNDZ);
	    mpz_fdiv_q_2exp(mpz(0), mpz(0), shift);
	    collect(2);
	    break;
	default:
	    abort();
    }

done:
    return (canonicalize());
}

eobject_t
eeval_add(void)
{
    eword_t		u, v, w;

    switch (etype(N(1))) {
	case t_word:
	    v = pwp(1);
	    w = pwp(2);
	    u = v + w;
	    if (eadd_over_p(u, v, w)) {
		get_mpz(0);
		mpz_set_si(mpz(0), v);
		if (w >= 0)
		    mpz_add_ui(mpz(0), mpz(0), w);
		else {
		    collect(1);
		    get_mpz(1);
		    mpz_set_si(mpz(1), w);
		    mpz_add(mpz(0), mpz(0), mpz(1));
		}
	    }
	    else {
		get_word(0);
		pwp(0) = u;
	    }
	    break;
	case t_float:
	    get_float(0);
	    pdp(0) = pdp(1) + pdp(2);
	    break;
	case t_rat:
	    get_rat(0);
	    if (!erat_add(rat(0), rat(1), rat(2))) {
		collect(0);
		get_mpq(0);
		get_mpq(3);
		get_mpq(4);
		mpq_set_si(mpq(3), erat_num(rat(1)), erat_den(rat(1)));
		mpq_set_si(mpq(4), erat_num(rat(2)), erat_den(rat(2)));
		mpq_add(mpq(0), mpq(3), mpq(4));
		collect(3);
		collect(4);
	    }
	    break;
	case t_mpz:
	    get_mpz(0);
	    mpz_add(mpz(0), mpz(1), mpz(2));
	    break;
	case t_mpq:
	    get_mpq(0);
	    mpq_add(mpq(0), mpq(1), mpq(2));
	    break;
	case t_mpr:
	    get_mpr(0);
	    mpfr_add(mpr(0), mpr(1), mpr(2), thr_rnd);
	    break;
	case t_cdd:
	    get_cdd(0);
	    pddp(0) = pddp(1) + pddp(2);
	    break;
	case t_cqq:
	    get_cqq(0);
	    cqq_add(cqq(0), cqq(1), cqq(2));
	    break;
	default:
	    get_mpc(0);
	    mpc_add(mpc(0), mpc(1), mpc(2), thr_rndc);
	    break;
    }

    return (canonicalize());
}

eobject_t
eeval_sub(void)
{
    eword_t		u, v, w;

    switch (etype(N(1))) {
	case t_word:
	    v = pwp(1);
	    w = pwp(2);
	    u = v - w;
	    if (esub_over_p(u, v, w)) {
		get_mpz(0);
		mpz_set_si(mpz(0), v);
		if (w >= 0)
		    mpz_sub_ui(mpz(0), mpz(0), w);
		else {
		    collect(1);
		    get_mpz(1);
		    mpz_set_si(mpz(1), w);
		    mpz_sub(mpz(0), mpz(0), mpz(1));
		}
	    }
	    else {
		get_word(0);
		pwp(0) = u;
	    }
	    break;
	case t_float:
	    get_float(0);
	    pdp(0) = pdp(1) - pdp(2);
	    break;
	case t_rat:
	    get_rat(0);
	    if (!erat_sub(rat(0), rat(1), rat(2))) {
		collect(0);
		get_mpq(0);
		get_mpq(3);
		get_mpq(4);
		mpq_set_si(mpq(3), erat_num(rat(1)), erat_den(rat(1)));
		mpq_set_si(mpq(4), erat_num(rat(2)), erat_den(rat(2)));
		mpq_sub(mpq(0), mpq(3), mpq(4));
		collect(3);
		collect(4);
	    }
	    break;
	case t_mpz:
	    get_mpz(0);
	    mpz_sub(mpz(0), mpz(1), mpz(2));
	    break;
	case t_mpq:
	    get_mpq(0);
	    mpq_sub(mpq(0), mpq(1), mpq(2));
	    break;
	case t_mpr:
	    get_mpr(0);
	    mpfr_sub(mpr(0), mpr(1), mpr(2), thr_rnd);
	    break;
	case t_cdd:
	    get_cdd(0);
	    pddp(0) = pddp(1) - pddp(2);
	    break;
	case t_cqq:
	    get_cqq(0);
	    cqq_sub(cqq(0), cqq(1), cqq(2));
	    break;
	default:
	    get_mpc(0);
	    mpc_sub(mpc(0), mpc(1), mpc(2), thr_rndc);
	    break;
    }

    return (canonicalize());
}

eobject_t
eeval_mul(void)
{
    eword_t		w;

    switch (etype(N(1))) {
	case t_word:
	    if ((w = ew_mul_w_w(pwp(1), pwp(2))) == MININT) {
		get_mpz(0);
		mpz_set_si(mpz(0), pwp(1));
		if (pwp(2) > 0)
		    mpz_mul_ui(mpz(0), mpz(0), pwp(2));
		else
		    mpz_mul_si(mpz(0), mpz(0), pwp(2));
	    }
	    else {
		get_word(0);
		pwp(0) = w;
	    }
	    break;
	case t_float:
	    get_float(0);
	    pdp(0) = pdp(1) * pdp(2);
	    break;
	case t_rat:
	    get_rat(0);
	    if (!erat_mul(rat(0), rat(1), rat(2))) {
		collect(0);
		get_mpq(0);
		get_mpq(3);
		get_mpq(4);
		mpq_set_si(mpq(3), erat_num(rat(1)), erat_den(rat(1)));
		mpq_set_si(mpq(4), erat_num(rat(2)), erat_den(rat(2)));
		mpq_mul(mpq(0), mpq(3), mpq(4));
		collect(3);
		collect(4);
	    }
	    break;
	case t_mpz:
	    get_mpz(0);
	    mpz_mul(mpz(0), mpz(1), mpz(2));
	    break;
	case t_mpq:
	    get_mpq(0);
	    mpq_mul(mpq(0), mpq(1), mpq(2));
	    break;
	case t_mpr:
	    get_mpr(0);
	    mpfr_mul(mpr(0), mpr(1), mpr(2), thr_rnd);
	    break;
	case t_cdd:
	    get_cdd(0);
	    pddp(0) = pddp(1) * pddp(2);
	    break;
	case t_cqq:
	    get_cqq(0);
	    cqq_mul(cqq(0), cqq(1), cqq(2));
	    break;
	default:
	    get_mpc(0);
	    mpc_mul(mpc(0), mpc(1), mpc(2), thr_rndc);
	    break;
    }

    return (canonicalize());
}

eobject_t
eeval_div(void)
{
    eword_t		u, v, w;

    switch (etype(N(1))) {
	case t_word:
	    v = pwp(1);
	    w = pwp(2);
	    u = v / w;
	    if (v == w * u) {
		get_word(0);
		pwp(0) = u;
	    }
	    else if (v != MININT && w != MININT) {
		get_rat(0);
		erat_set_si(rat(0), v, w);
	    }
	    else {
		get_mpq(0);
		mpq_set_si(mpq(0), v, w);
		mpq_canonicalize(mpq(0));
	    }
	    break;
	case t_float:
	    get_float(0);
	    pdp(0) = pdp(1) / pdp(2);
	    break;
	case t_rat:
	    get_rat(0);
	    if (!erat_div(rat(0), rat(1), rat(2))) {
		collect(0);
		get_mpq(0);
		get_mpq(3);
		get_mpq(4);
		mpq_set_si(mpq(3), erat_num(rat(1)), erat_den(rat(1)));
		mpq_set_si(mpq(4), erat_num(rat(2)), erat_den(rat(2)));
		mpq_div(mpq(0), mpq(3), mpq(4));
		collect(3);
		collect(4);
	    }
	    break;
	case t_mpz:
	    get_mpq(0);
	    mpz_set(mpq_numref(mpq(0)), mpz(1));
	    mpz_set(mpq_denref(mpq(0)), mpz(2));
	    mpq_canonicalize(mpq(0));
	    break;
	case t_mpq:
	    get_mpq(0);
	    mpq_div(mpq(0), mpq(1), mpq(2));
	    break;
	case t_mpr:
	    get_mpr(0);
	    mpfr_div(mpr(0), mpr(1), mpr(2), thr_rnd);
	    break;
	case t_cdd:
	    get_cdd(0);
	    pddp(0) = pddp(1) / pddp(2);
	    break;
	case t_cqq:
	    get_cqq(0);
	    cqq_div(cqq(0), cqq(1), cqq(2));
	    break;
	default:
	    get_mpc(0);
	    mpc_div(mpc(0), mpc(1), mpc(2), thr_rndc);
	    break;
    }

    return (canonicalize());
}

eobject_t
eeval_trunc2(void)
{
    eword_t		w;
    efloat_t		d;

    switch (etype(N(1))) {
	case t_word:
	    get_word(0);
	    pwp(0) = pwp(1) / pwp(2);
	    break;
	case t_float:
	    modf(pdp(1) / pdp(2), &d);
	    if (d == (eword_t)d) {
		get_word(0);
		pwp(0) = d;
	    }
	    else {
		get_mpz(0);
		mpz_set_d(mpz(0), d);
	    }
	    break;
	case t_rat:
	    get_rat(0);
	    if (!erat_div(rat(0), rat(1), rat(2))) {
		collect(0);
		get_mpz(0);
		get_mpq(3);
		get_mpq(4);
		mpq_set_si(mpq(3), erat_num(rat(1)), erat_den(rat(1)));
		mpq_set_si(mpq(4), erat_num(rat(2)), erat_den(rat(2)));
		mpq_div(mpq(3), mpq(3), mpq(4));
		mpz_tdiv_q(mpz(0), mpq_numref(mpq(3)), mpq_denref(mpq(3)));
		collect(3);
		collect(4);
	    }
	    else {
		w = erat_num(rat(0)) / erat_den(rat(0));
		collect(0);
		get_word(0);
		pwp(0) = w;
	    }
	    break;
	case t_mpz:
	    get_mpz(0);
	    mpz_tdiv_q(mpz(0), mpz(1), mpz(2));
	    break;
	case t_mpq:
	    get_mpq(3);
	    mpq_div(mpq(3), mpq(1), mpq(2));
	    get_mpz(0);
	    mpz_tdiv_q(mpz(0), mpq_numref(mpq(3)), mpq_denref(mpq(3)));
	    collect(3);
	    break;
	case t_mpr:
	    get_mpr(3);
	    mpfr_div(mpr(3), mpr(1), mpr(2), thr_rnd);
	    mpfr_trunc(mpr(3), mpr(3));
	    get_mpz(0);
	    mpfr_get_z(mpz(0), mpr((3)), GMP_RNDZ);
	    collect(3);
	    break;
	default:
	    abort();
    }

    return (canonicalize());
}

eobject_t
eeval_rem(void)
{
    eword_t		w;

    switch (etype(N(1))) {
	case t_word:
	    get_word(0);
	    pwp(0) = pwp(1) % pwp(2);
	    break;
	case t_float:
	    get_float(0);
	    pdp(0) = fmod(pdp(1), pdp(2));
	    break;
	case t_rat:
	    get_rat(0);
	    if (!erat_div(rat(0), rat(1), rat(2))) {
		collect(0);
		get_mpq(0);
		get_mpq(3);
		get_mpq(4);
		mpq_set_si(mpq(3), erat_num(rat(1)), erat_den(rat(1)));
		mpq_set_si(mpq(4), erat_num(rat(2)), erat_den(rat(2)));
		mpq_div(mpq(0), mpq(3), mpq(4));
		mpz_tdiv_r(mpq_numref(mpq(0)),
			   mpq_numref(mpq(0)), mpq_denref(mpq(0)));
		mpz_mul(mpq_denref(mpq(0)),
			mpq_denref(mpq(3)), mpq_denref(mpq(4)));
		mpq_canonicalize(mpq(0));
		if (erat_num(rat(2)) < 0)
		    mpq_neg(mpq(0), mpq(0));
		collect(3);
		collect(4);
	    }
	    else {
		w = erat_num(rat(0)) % erat_den(rat(0));
		erat_num(rat(0)) = w;
		w = ew_mul_w_w(erat_den(rat(1)), erat_den(rat(2)));
		erat_den(rat(0)) = erat_num(rat(2)) < 0 ? -w : w;
	    }
	    break;
	case t_mpz:
	    get_mpz(0);
	    mpz_tdiv_r(mpz(0), mpz(1), mpz(2));
	    break;
	case t_mpq:
	    get_mpq(0);
	    mpq_div(mpq(0), mpq(1), mpq(2));
	    mpz_tdiv_r(mpq_numref(mpq(0)),
		       mpq_numref(mpq(0)), mpq_denref(mpq(0)));
	    mpz_mul(mpq_denref(mpq(0)),
		    mpq_denref(mpq(1)), mpq_denref(mpq(2)));
	    mpq_canonicalize(mpq(0));
	    if (mpq_sgn(mpq(2)) < 0)
		mpq_neg(mpq(0), mpq(0));
	    break;
	case t_mpr:
	    get_mpr(0);
	    mpfr_fmod(mpr(0), mpr(1), mpr(2), thr_rnd);
	    break;
	default:
	    abort();
    }

    return (canonicalize());
}

eobject_t
eeval_complex(void)
{
    switch (etype(N(1))) {
	case t_word:
	    get_cqq(0);
	    cqq_set_si(cqq(0), pwp(1), pwp(2));
	    break;
	case t_float:
	    get_cdd(0);
	    real(pddp(0)) = pdp(1);
	    imag(pddp(0)) = pdp(2);
	    break;
	case t_rat:
	    get_cqq(0);
	    mpq_set_si(cqq_realref(cqq(0)), erat_num(rat(1)), erat_den(rat(1)));
	    mpq_set_si(cqq_imagref(cqq(0)), erat_num(rat(2)), erat_den(rat(2)));
	    break;
	case t_mpz:
	    get_cqq(0);
	    mpq_set_z(cqq_realref(cqq(0)), mpz(1));
	    mpq_set_z(cqq_imagref(cqq(0)), mpz(2));
	    break;
	case t_mpq:
	    get_cqq(0);
	    mpq_set(cqq_realref(cqq(0)), mpq(1));
	    mpq_set(cqq_imagref(cqq(0)), mpq(2));
	    break;
	case t_mpr:
	    get_mpc(0);
	    mpfr_set(mpc_realref(mpc(0)), mpr(1), thr_rnd);
	    mpfr_set(mpc_imagref(mpc(0)), mpr(2), thr_rnd);
	    break;
	default:
	    abort();
    }

    return (canonicalize());
}

static void
eval_unary_setup(eobject_t value)
{
    if (value)
	N(1) = value;
    else {
	coerced(1);	get_word(1);	pwp(1) = 0;
    }
}

void
eval_binary_setup(eobject_t lvalue, eobject_t rvalue)
{
    etype_t		ltype;
    etype_t		rtype;

    if (lvalue)		ltype = etype(lvalue);
    else		ltype = t_void;
    if (rvalue)		rtype = etype(rvalue);
    else		rtype = t_void;
    switch (ltype) {
	case t_void:
	    switch (rtype) {
		case t_void:
		    coerced(1);		get_word(1);
		    pwp(1) = 0;
		    coerced(2);		get_word(2);
		    pwp(2) = 0;
		    break;
		case t_word:
		    coerced(1);		get_word(1);
		    pwp(1) = 0;
		    N(2) = rvalue;
		    break;
		case t_float:
		    coerced(1);		get_float(1);
		    pdp(1) = 0.0;
		    N(2) = rvalue;
		    break;
		case t_rat:
		    coerced(1);		get_rat(1);
		    erat_set_si(rat(1), 0, 1);
		    N(2) = rvalue;
		    break;
		case t_mpz:
		    coerced(1);		get_mpz(1);
		    mpz_set_ui(mpz(1), 0);
		    N(2) = rvalue;
		    break;
		case t_mpq:
		    coerced(1);		get_mpq(1);
		    mpq_set_ui(mpq(1), 0, 1);
		    N(2) = rvalue;
		    break;
		case t_mpr:
		    coerced(1);		get_mpr(1);
		    mpfr_set_ui(mpr(1), 0, thr_rnd);
		    N(2) = rvalue;
		    break;
		case t_cdd:
		    coerced(1);		get_cdd(1);
		    pddp(1) = 0.0;
		    N(2) = rvalue;
		    break;
		case t_cqq:
		    coerced(1);		get_cqq(1);
		    cqq_set_ui(cqq(1), 0, 1);
		    N(2) = rvalue;
		    break;
		default:
		    assert(rtype == t_mpc);
		    coerced(1);		get_mpc(1);
		    mpc_set_ui(mpc(1), 0, thr_rndc);
		    N(2) = rvalue;
		    break;
	    }
	    break;
	case t_word:
	    switch (rtype) {
		case t_void:
		    N(1) = lvalue;
		    coerced(2);		get_word(2);
		    pwp(2) = 0;
		    break;
		case t_word:
		    N(1) = lvalue;
		    N(2) = rvalue;
		    break;
		case t_float:
		    coerced(1);		get_float(1);
		    pdp(1) = _pwp(lvalue);
		    N(2) = rvalue;
		    break;
		case t_rat:
		    if (unlikely(_pwp(lvalue) == MININT)) {
			coerced(1);	get_mpq(1);
			mpq_set_si(mpq(1), _pwp(lvalue), 1);
			coerced(2);	get_mpq(2);
			mpq_set_si(mpq(2),
				   erat_num(_rat(rvalue)),
				   erat_den(_rat(rvalue)));
		    }
		    else {
			coerced(1);	get_rat(1);
			erat_set_si(rat(1), _pwp(lvalue), 1);
			N(2) = rvalue;
		    }
		    break;
		case t_mpz:
		    coerced(1);		get_mpz(1);
		    mpz_set_si(mpz(1), _pwp(lvalue));
		    N(2) = rvalue;
		    break;
		case t_mpq:
		    coerced(1);		get_mpq(1);
		    mpq_set_si(mpq(1), _pwp(lvalue), 1);
		    N(2) = rvalue;
		    break;
		case t_mpr:
		    coerced(1);		get_mpr(1);
		    mpfr_set_si(mpr(1), _pwp(lvalue), thr_rnd);
		    N(2) = rvalue;
		    break;
		case t_cdd:
		    coerced(1);		get_cdd(1);
		    pddp(1) = _pwp(lvalue);
		    N(2) = rvalue;
		    break;
		case t_cqq:
		    coerced(1);		get_cqq(1);
		    cqq_set_si(cqq(2), _pwp(lvalue), 1);
		    N(2) = rvalue;
		    break;
		default:
		    assert(rtype == t_mpc);
		    coerced(1);		get_mpc(1);
		    mpc_set_si(mpc(1), 1, thr_rndc);
		    N(2) = rvalue;
		    break;
	    }
	    break;
	case t_float:
	    switch (rtype) {
		case t_void:
		    N(1) = lvalue;
		    coerced(2);		get_float(2);
		    pdp(2) = 0.0;
		    break;
		case t_word:
		    N(1) = lvalue;
		    coerced(2);		get_float(2);
		    pdp(2) = _pwp(rvalue);
		    break;
		case t_float:
		    N(1) = lvalue;
		    N(2) = rvalue;
		    break;
		case t_rat:
		    N(1) = lvalue;
		    coerced(2);		get_float(2);
		    pdp(2) = erat_get_d(_rat(rvalue));
		    break;
		case t_mpz:
		    N(1) = lvalue;
		    coerced(2);		get_float(2);
		    pdp(2) = mpz_get_d(_mpz(rvalue));
		    break;
		case t_mpq:
		    N(1) = lvalue;
		    coerced(2);		get_float(2);
		    pdp(2) = mpq_get_d(_mpq(rvalue));
		    break;
		case t_mpr:
		    coerced(1);		get_mpr(1);
		    mpfr_set_d(mpr(1), _pdp(lvalue), thr_rnd);
		    N(2) = rvalue;
		    break;
		case t_cdd:
		    coerced(1);		get_cdd(1);
		    pddp(1) = _pdp(lvalue);
		    N(2) = rvalue;
		    break;
		case t_cqq:
		    coerced(1);		get_cdd(1);
		    pddp(1) = _pdp(lvalue);
		    coerced(2);		get_cdd(2);
		    real(pddp(2)) = mpq_get_d(cqq_realref(_cqq(rvalue)));
		    imag(pddp(2)) = mpq_get_d(cqq_imagref(_cqq(rvalue)));
		    break;
		default:
		    assert(rtype == t_mpc);
		    coerced(1);		get_mpc(1);
		    mpc_set_d(mpc(1), _pdp(lvalue), thr_rndc);
		    N(2) = rvalue;
		    break;
	    }
	    break;
	case t_rat:
	    switch (rtype) {
		case t_void:
		    N(1) = lvalue;
		    coerced(2);		get_rat(2);
		    erat_set_si(rat(2), 0, 1);
		    break;
		case t_word:
		    if (unlikely(_pwp(rvalue) == MININT)) {
			coerced(1);	get_mpq(1);
			mpq_set_si(mpq(1),
				   erat_num(_rat(lvalue)),
				   erat_den(_rat(lvalue)));
			coerced(2);	get_mpq(2);
			mpq_set_si(mpq(2), _pwp(rvalue), 1);
		    }
		    else {
			N(1) = lvalue;
			coerced(2);	get_rat(2);
			erat_set_si(rat(2), _pwp(rvalue), 1);
		    }
		    break;
		case t_float:
		    coerced(1);		get_float(1);
		    pdp(1) = erat_get_d(_rat(lvalue));
		    N(2) = rvalue;
		    break;
		case t_rat:
		    N(1) = lvalue;
		    N(2) = rvalue;
		    break;
		case t_mpz:
		    coerced(1);		get_mpq(1);
		    mpq_set_si(mpq(1),
			       erat_num(_rat(lvalue)),
			       erat_den(_rat(lvalue)));
		    coerced(2);		get_mpq(2);
		    mpq_set_z(mpq(2), _mpz(rvalue));
		    break;
		case t_mpq:
		    coerced(1);		get_mpq(1);
		    mpq_set_si(mpq(1),
			       erat_num(_rat(lvalue)),
			       erat_den(_rat(lvalue)));
		    N(2) = rvalue;
		    break;
		case t_mpr:
		    coerced(1);		get_mpr(1);
		    mpfr_set_si(mpr(1), erat_num(_rat(lvalue)), thr_rnd);
		    coerced(2);		get_mpr(2);
		    mpfr_set_si(mpr(2), erat_den(_rat(lvalue)), thr_rnd);
		    mpfr_div(mpr(1), mpr(1), mpr(2), thr_rnd);
		    collect(2);
		    N(2) = rvalue;
		    break;
		case t_cdd:
		    coerced(1);		get_cdd(1);
		    pddp(1) = erat_get_d(_rat(lvalue));
		    N(2) = rvalue;
		    break;
		case t_cqq:
		    coerced(1);		get_cqq(1);
		    cqq_set_si(cqq(1),
			       erat_num(_rat(lvalue)),
			       erat_den(_rat(lvalue)));
		    N(2) = rvalue;
		    break;
		default:
		    assert(rtype == t_mpc);
		    coerced(1);		get_mpc(1);
		    coerced(2);		get_mpr(2);
		    mpfr_set_si(mpc_realref(mpc(1)), erat_num(_rat(lvalue)),
				thr_rnd);
		    mpfr_set_si(mpr(2), erat_den(_rat(lvalue)), thr_rnd);
		    mpfr_div(mpc_realref(mpc(1)), mpc_realref(mpc(1)),
			     mpr(2), thr_rnd);
		    mpfr_set_ui(mpc_imagref(mpc(1)), 0, thr_rnd);
		    collect(2);
		    N(2) = rvalue;
		    break;
	    }
	    break;
	case t_mpz:
	    switch (rtype) {
		case t_void:
		    N(1) = lvalue;
		    coerced(2);		get_mpz(2);
		    mpz_set_ui(mpz(2), 0);
		    break;
		case t_word:
		    N(1) = lvalue;
		    coerced(2);		get_mpz(2);
		    mpz_set_si(mpz(2), _pwp(rvalue));
		    break;
		case t_float:
		    coerced(1);		get_float(1);
		    pdp(1) = mpz_get_d(_mpz(lvalue));
		    N(2) = rvalue;
		    break;
		case t_rat:
		    coerced(1);		get_mpq(1);
		    mpq_set_z(mpq(1), _mpz(lvalue));
		    coerced(2);		get_mpq(2);
		    mpq_set_si(mpq(2),
			       erat_num(_rat(rvalue)),
			       erat_den(_rat(rvalue)));
		    break;
		case t_mpz:
		    N(1) = lvalue;
		    N(2) = rvalue;
		    break;
		case t_mpq:
		    coerced(1);		get_mpq(1);
		    mpq_set_z(mpq(1), _mpz(lvalue));
		    N(2) = rvalue;
		    break;
		case t_mpr:
		    coerced(1);		get_mpr(1);
		    mpfr_set_z(mpr(1), _mpz(lvalue), thr_rnd);
		    N(2) = rvalue;
		    break;
		case t_cdd:
		    coerced(1);		get_cdd(1);
		    pddp(1) = mpz_get_d(_mpz(lvalue));
		    N(2) = rvalue;
		    break;
		case t_cqq:
		    coerced(1);		get_cqq(1);
		    cqq_set_z(cqq(1), _mpz(lvalue));
		    N(2) = rvalue;
		    break;
		default:
		    assert(rtype == t_mpc);
		    coerced(1);		get_mpc(1);
		    mpc_set_z(mpc(1), _mpz(lvalue), thr_rndc);
		    N(2) = rvalue;
		    break;
	    }
	    break;
	case t_mpq:
	    switch (rtype) {
		case t_void:
		    N(1) = lvalue;
		    coerced(2);		get_mpq(2);
		    mpq_set_ui(mpq(2), 0, 1);
		    break;
		case t_word:
		    N(1) = lvalue;
		    coerced(2);		get_mpq(2);
		    mpq_set_ui(mpq(2), _pwp(rvalue), 1);
		    break;
		case t_float:
		    coerced(1);		get_float(1);
		    pdp(1) = mpq_get_d(_mpq(lvalue));
		    N(2) = rvalue;
		    break;
		case t_rat:
		    N(1) = lvalue;
		    coerced(2);		get_mpq(2);
		    mpq_set_si(mpq(2),
			       erat_num(_rat(rvalue)),
			       erat_den(_rat(rvalue)));
		    break;
		case t_mpz:
		    N(1) = lvalue;
		    coerced(2);		get_mpq(2);
		    mpq_set_z(mpq(2), _mpz(rvalue));
		    break;
		case t_mpq:
		    N(1) = lvalue;
		    N(2) = rvalue;
		    break;
		case t_mpr:
		    coerced(1);		get_mpr(1);
		    mpfr_set_q(mpr(1), _mpq(lvalue), thr_rnd);
		    N(2) = rvalue;
		    break;
		case t_cdd:
		    coerced(1);		get_cdd(1);
		    pddp(1) = mpq_get_d(_mpq(lvalue));
		    N(2) = rvalue;
		    break;
		case t_cqq:
		    coerced(1);		get_cqq(1);
		    cqq_set_q(cqq(1), _mpq(lvalue));
		    N(2) = rvalue;
		    break;
		default:
		    assert(rtype == t_mpc);
		    coerced(1);		get_mpc(1);
		    mpc_set_q(mpc(1), _mpq(lvalue), thr_rndc);
		    N(2) = rvalue;
		    break;
	    }
	    break;
	case t_mpr:
	    switch (rtype) {
		case t_void:
		    N(1) = lvalue;
		    coerced(2);		get_mpr(2);
		    mpfr_set_ui(mpr(2), 0, thr_rnd);
		    break;
		case t_word:
		    N(1) = lvalue;
		    coerced(2);		get_mpr(2);
		    mpfr_set_si(mpr(2), _pwp(rvalue), thr_rnd);
		    break;
		case t_float:
		    N(1) = lvalue;
		    coerced(2);		get_mpr(2);
		    mpfr_set_d(mpr(2), _pdp(rvalue), thr_rnd);
		    break;
		case t_rat:
		    N(1) = lvalue;
		    coerced(2);		get_mpr(2);
		    mpfr_set_si(mpr(2), erat_num(_rat(rvalue)), thr_rnd);
		    get_mpr(3);
		    mpfr_set_si(mpr(3), erat_den(_rat(rvalue)), thr_rnd);
		    mpfr_div(mpr(2), mpr(2), mpr(3), thr_rnd);
		    collect(3);
		    break;
		case t_mpz:
		    N(1) = lvalue;
		    coerced(2);		get_mpr(2);
		    mpfr_set_z(mpr(2), _mpz(rvalue), thr_rnd);
		    break;
		case t_mpq:
		    N(1) = lvalue;
		    coerced(1);		get_mpr(2);
		    mpfr_set_q(mpr(2), _mpq(rvalue), thr_rnd);
		    break;
		case t_mpr:
		    N(1) = lvalue;
		    N(2) = rvalue;
		    break;
		case t_cdd:
		    coerced(1);		get_mpc(1);
		    mpc_set_fr(mpc(1), _mpr(lvalue), thr_rndc);
		    coerced(2);		get_mpc(2);
		    mpc_set_dc(mpc(2), _pddp(rvalue), thr_rndc);
		    break;
		case t_cqq:
		    coerced(1);		get_mpc(1);
		    mpc_set_fr(mpc(1), _mpr(lvalue), thr_rndc);
		    coerced(2);		get_mpc(2);
		    mpfr_set_q(mpc_realref(mpc(2)), cqq_realref(_cqq(rvalue)),
			       thr_rnd);
		    mpfr_set_q(mpc_imagref(mpc(2)), cqq_imagref(_cqq(rvalue)),
			       thr_rnd);
		    break;
		default:
		    assert(rtype == t_mpc);
		    coerced(1);		get_mpc(1);
		    mpc_set_fr(mpc(1), _mpr(lvalue), thr_rndc);
		    N(2) = rvalue;
		    break;
	    }
	    break;
	case t_cdd:
	    switch (rtype) {
		case t_void:
		    N(1) = lvalue;
		    coerced(2);		get_cdd(2);
		    pddp(2) = 0.0;
		    break;
		case t_word:
		    N(1) = lvalue;
		    coerced(2);		get_cdd(2);
		    pddp(2) = _pwp(rvalue);
		    break;
		case t_float:
		    N(1) = lvalue;
		    coerced(2);		get_cdd(2);
		    pddp(2) = _pdp(rvalue);
		    break;
		case t_rat:
		    N(1) = lvalue;
		    coerced(2);		get_cdd(2);
		    pddp(2) = erat_get_d(_rat(lvalue));
		    break;
		case t_mpz:
		    N(1) = lvalue;
		    coerced(2);		get_cdd(2);
		    pddp(2) = mpz_get_d(_mpz(rvalue));
		    break;
		case t_mpq:
		    N(1) = lvalue;
		    coerced(2);		get_cdd(2);
		    pddp(2) = mpq_get_d(_mpq(rvalue));
		    break;
		case t_mpr:
		    coerced(1);		get_mpc(1);
		    mpc_set_dc(mpc(1), _pddp(lvalue), thr_rndc);
		    coerced(2);		get_mpc(2);
		    mpc_set_fr(mpc(2), _mpr(rvalue), thr_rndc);
		    break;
		case t_cdd:
		    N(1) = lvalue;
		    N(2) = rvalue;
		    break;
		case t_cqq:
		    N(1) = lvalue;
		    coerced(2);		get_cdd(2);
		    real(pddp(2)) = mpq_get_d(cqq_realref(_cqq(rvalue)));
		    imag(pddp(2)) = mpq_get_d(cqq_imagref(_cqq(rvalue)));
		    break;
		default:
		    assert(rtype == t_mpc);
		    coerced(1);		get_mpc(1);
		    mpc_set_dc(mpc(1), _pddp(lvalue), thr_rndc);
		    N(2) = rvalue;
		    break;
	    }
	    break;
	case t_cqq:
	    switch (rtype) {
		case t_void:
		    N(1) = lvalue;
		    coerced(2);		get_cqq(2);
		    cqq_set_ui(cqq(2), 1, 0);
		    break;
		case t_word:
		    N(1) = lvalue;
		    coerced(2);		get_cqq(2);
		    cqq_set_si(cqq(2), _pwp(rvalue), 0);
		    break;
		case t_float:
		    coerced(1);		get_cdd(1);
		    real(pddp(1)) = mpq_get_d(cqq_realref(_cqq(lvalue)));
		    imag(pddp(1)) = mpq_get_d(cqq_imagref(_cqq(lvalue)));
		    coerced(2);		get_cdd(2);
		    pddp(2) = _pdp(rvalue);
		    break;
		case t_rat:
		    N(1) = rvalue;
		    coerced(2);		get_cqq(2);
		    cqq_set_si(cqq(2),
			       erat_num(_rat(rvalue)),
			       erat_den(_rat(rvalue)));
		    break;
		case t_mpz:
		    N(1) = lvalue;
		    coerced(2);		get_cqq(2);
		    cqq_set_z(cqq(2), _mpz(rvalue));
		    break;
		case t_mpq:
		    N(1) = lvalue;
		    coerced(2);		get_cqq(2);
		    cqq_set_q(cqq(2), _mpq(rvalue));
		    break;
		case t_mpr:
		    get_mpc(1);
		    mpfr_set_q(mpc_realref(mpc(1)), cqq_realref(_cqq(lvalue)),
			       thr_rnd);
		    mpfr_set_q(mpc_imagref(mpc(1)), cqq_imagref(_cqq(lvalue)),
			       thr_rnd);
		    coerced(2);		get_mpc(2);
		    mpc_set_fr(mpc(2), _mpr(rvalue), thr_rndc);
		    break;
		case t_cdd:
		    coerced(1);		get_cdd(1);
		    real(pddp(1)) = mpq_get_d(cqq_realref(_cqq(lvalue)));
		    imag(pddp(1)) = mpq_get_d(cqq_imagref(_cqq(lvalue)));
		    N(2) = rvalue;
		    break;
		case t_cqq:
		    N(1) = lvalue;
		    N(2) = rvalue;
		    break;
		default:
		    assert(rtype == t_mpc);
		    coerced(1);		get_mpc(1);
		    mpfr_set_q(mpc_realref(mpc(1)), cqq_realref(_cqq(lvalue)),
			       thr_rnd);
		    mpfr_set_q(mpc_imagref(mpc(1)), cqq_imagref(_cqq(lvalue)),
			       thr_rnd);
		    N(2) = rvalue;
		    break;
	    }
	    break;
	default:
	    assert(ltype == t_mpc);
	    N(1) = lvalue;
	    switch (rtype) {
		case t_void:
		    coerced(2);		get_mpc(2);
		    mpc_set_ui(mpc(2), 0, thr_rndc);
		    break;
		case t_word:
		    coerced(2);		get_mpc(2);
		    mpc_set_si(mpc(2), _pwp(lvalue), thr_rndc);
		    break;
		case t_float:
		    coerced(2);		get_mpc(2);
		    mpc_set_d(mpc(2), _pdp(rvalue), thr_rndc);
		    break;
		case t_rat:
		    coerced(2);		get_mpc(2);
		    get_mpr(3);
		    mpfr_set_si(mpc_realref(mpc(2)), erat_num(_rat(rvalue)),
				thr_rnd);
		    mpfr_set_si(mpr(3), erat_den(_rat(rvalue)), thr_rnd);
		    mpfr_div(mpc_realref(mpc(2)), mpc_realref(mpc(2)),
			     mpr(3), thr_rnd);
		    mpfr_set_ui(mpc_imagref(mpc(2)), 0, thr_rnd);
		    collect(3);
		    break;
		case t_mpz:
		    coerced(2);		get_mpc(2);
		    mpc_set_z(mpc(2), _mpz(rvalue), thr_rndc);
		    break;
		case t_mpq:
		    coerced(2);		get_mpc(2);
		    mpc_set_q(mpc(2), _mpq(rvalue), thr_rndc);
		    break;
		case t_mpr:
		    coerced(2);		get_mpc(2);
		    mpc_set_fr(mpc(2), _mpr(rvalue), thr_rndc);
		    break;
		case t_cdd:
		    coerced(2);		get_mpc(2);
		    mpc_set_dc(mpc(2), _pddp(rvalue), thr_rndc);
		    break;
		case t_cqq:
		    coerced(2);		get_mpc(2);
		    mpfr_set_q(mpc_realref(mpc(2)), cqq_realref(_cqq(rvalue)),
			       thr_rnd);
		    mpfr_set_q(mpc_imagref(mpc(2)), cqq_imagref(_cqq(rvalue)),
			       thr_rnd);
		    break;
		default:
		    assert(rtype == t_mpc);
		    N(2) = rvalue;
		    break;
	    }
	    break;
    }
}

static void
check_real(east_t *ast)
{
    if (ast->l.value) {
	switch (etype(ast->l.value)) {
	    case t_word:	case t_float:		case t_rat:
	    case t_mpz:		case t_mpq:		case t_mpr:
		break;
	    default:
		eparse_error(ast, "not a real number %A", ast);
	}
    }
}

static void
check_integer(east_t *ast)
{
    etype_t		type;

    if (ast->l.value) {
	type = etype(ast->l.value);
	if (type != t_word && type != t_mpz)
	    eparse_error(ast, "not an integer %A", ast);
    }
}

static eint32_t
shift_power(east_t *ast)
{
    eint32_t		 shift;
    union {
	eword_t		*wrd;
	efloat_t	*flt;
	empr_t		 mpr;
	eobject_t	 obj;
    } value;

    if ((value.obj = ast->l.value) == null)
	return (0);
#if __WORDSIZE == 32
    switch (etype(value.obj)) {
	case t_word:
	    shift = *value.wrd;
	    if (shift != MININT)
		return (shift);
	    break;
	case t_float:
	    shift = *value.flt;
	    if (shift == *value.flt && shift != MININT)
		return (shift);
	    break;
	case t_mpr:
	    if (mpfr_integer_p(value.mpr) &&
		mpfr_fits_sint_p(value.mpr, GMP_RNDZ) &&
		(shift = mpfr_get_si(value.mpr, thr_rnd)) != MININT)
		return (shift);
	    break;
    }
#else
    switch (etype(value.obj)) {
	case t_word:
	    shift = *value.wrd;
	    if ((eint32_t)shift == *value.wrd)
		return (shift);
	    break;
	case t_float:
	    shift = *value.flt;
	    if ((eint32_t)shift == *value.flt)
		return (shift);
	    break;
	case t_mpr:
	    if (mpfr_integer_p(value.mpr) &&
		mpfr_fits_sint_p(value.mpr, GMP_RNDZ))
		return (mpfr_get_si(value.mpr, thr_rnd));
	    break;
    }
#endif

    eparse_error(ast, "not a 32 bit integer %A", ast);
}

static eobject_t
canonicalize(void)
{
    eword_t		w;
    efloat_t		d;
    empz_t		z;
    empq_t		q;
    rat_t		r;
    ecqq_t		qq;
    empc_t		cc;

    switch (etype(N(0))) {
	case t_rat:
	    w = erat_canonicalize(rat(0));
	    assert(w != 0);
	    if (erat_den(rat(0)) == 1) {
		w = erat_num(rat(0));
		collect(0);
		get_word(0);
		pwp(0) = w;
	    }
	    else if (erat_num(rat(0)) == 0) {
		collect(0);
		get_word(0);
		pwp(0) = 0;
	    }
	    break;
	case t_mpz:
	    z = mpz(0);
	    if (mpz_fits_slong_p(z)) {
		w = mpz_get_si(z);
		collect(0);
		get_word(0);
		pwp(0) = w;
	    }
	    break;
	case t_mpq:
	    q = mpq(0);
	    if (mpz_cmp_ui(mpq_denref(q), 1) == 0) {
		z = mpq_numref(q);
		if (mpz_fits_slong_p(z)) {
		    w = mpz_get_si(z);
		    collect(0);
		    get_word(0);
		    pwp(0) = w;
		}
		else {
		    collect(0);
		    get_mpz(0);
		    mpz_set(mpz(0), z);
		}
	    }
	    else if (mpz_cmp_ui(mpq_numref(q), 0) == 0) {
		collect(0);
		get_word(0);
		pwp(0) = 0;
	    }
	    else if (mpz_fits_slong_p(mpq_numref(q)) &&
		     mpz_fits_slong_p(mpq_denref(q))) {
		rat_num(r) = mpz_get_si(mpq_numref(q));
		rat_den(r) = mpz_get_si(mpq_denref(q));
		if (rat_num(r) != MININT && rat_den(r) != MININT) {
		    collect(0);
		    get_rat(0);
		    erat_num(rat(0)) = rat_num(r);
		    erat_den(rat(0)) = rat_den(r);
		}
	    }
	    break;
	case t_cdd:
	    if (imag(pddp(0)) == 0) {
		d = real(pddp(0));
		collect(0);
		get_float(0);
		pdp(0) = d;
	    }
	    break;
	case t_cqq:
	    qq = cqq(0);
	    if (mpq_sgn(cqq_imagref(qq)) == 0) {
		q = cqq_realref(qq);
		if (mpz_cmp_ui(mpq_denref(q), 1) == 0) {
		    z = mpq_numref(q);
		    if (mpz_fits_slong_p(z)) {
			w = mpz_get_si(z);
			collect(0);
			get_word(0);
			pwp(0) = w;
		    }
		    else {
			collect(0);
			get_mpz(0);
			mpz_set(mpz(0), z);
		    }
		}
		else if (mpz_cmp_ui(mpq_numref(q), 0) == 0) {
		    collect(0);
		    get_word(0);
		    pwp(0) = 0;
		}
		else if (mpz_fits_slong_p(mpq_numref(q)) &&
			 mpz_fits_slong_p(mpq_denref(q))) {
		    rat_num(r) = mpz_get_si(mpq_numref(q));
		    rat_den(r) = mpz_get_si(mpq_denref(q));
		    if (rat_num(r) != MININT && rat_den(r) != MININT) {
			collect(0);
			get_rat(0);
			erat_num(rat(0)) = rat_num(r);
			erat_den(rat(0)) = rat_den(r);
		    }
		}
		else {
		    collect(0);
		    get_mpq(0);
		    mpq_set(mpq(0), q);
		}
	    }
	    break;
	case t_mpc:
	    cc = mpc(0);
	    if (mpfr_zero_p(mpc_imagref(cc))) {
		collect(0);
		get_mpr(0);
		mpfr_set(mpr(0), mpc_realref(cc), thr_rnd);
	    }
	    break;
	default:
	    break;
    }

    return (cleanup());
}

static eobject_t
cleanup(void)
{
    if (cache_mask & (1 << 1))
	edel_object(P(1));
    if (cache_mask & (1 << 2))
	edel_object(P(2));
    cache_mask = 0;

    return (N(0));
}
